import type {
  ThemeSetting,
  InferData,
  KeyValueData,
  UpdateInferData,
  UpdateKeyValueData,
} from "./type";
import tinycolor from "@ctrl/tinycolor";
// 引入默认推断数据
import inferData from "./defaultInferData";
// 引入默认keyValue数据
import keyValueData from "./defaultKeyValueData";
// 引入设置对象
import setting from "./setting";
import type { App } from "vue";
declare global {
  interface ChildNode {
    innerText: string;
  }
}
class Theme {
  /**
   * 主题设置
   */
  themeSetting: ThemeSetting;
  /**
   * 键值数据
   */
  keyValue: KeyValueData;
  /**
   * 外推数据
   */
  inferData: Array<InferData>;
  /**
   *是否是第一次初始化
   */
  isFirstWriteStyle: boolean;
  /**
   * 混色白
   */
  colorWhite: string;
  /**
   * 混色黑
   */
  colorBlack: string;

  constructor(
    themeSetting: ThemeSetting,
    keyValue: KeyValueData,
    inferData: Array<InferData>
  ) {
    this.themeSetting = themeSetting;
    this.keyValue = keyValue;
    this.inferData = inferData;
    this.isFirstWriteStyle = true;
    this.colorWhite = "#ffffff";
    this.colorBlack = "#000000";
    this.initDefaultTheme();
  }

  /**
   * 拼接
   * @param setting 主题设置
   * @param names   需要拼接的所有值
   * @returns       拼接后的数据
   */
  getVarName = (setting: ThemeSetting, ...names: Array<string>) => {
    return (
      setting.startDivision +
      setting.namespace +
      setting.division +
      names.join(setting.division)
    );
  };

  /**
   * 转换外推数据
   * @param setting      主题设置对象
   * @param inferData    外推数据
   * @returns
   */
  mapInferMainStyle = (setting: ThemeSetting, inferData: InferData) => {
    const key: string = this.getVarName(
      setting,
      inferData.setting
        ? inferData.setting.type
        : setting.colorInferSetting.type,
      inferData.key
    );
    return {
      [key]: inferData.value,
      ...this.mapInferDataStyle(setting, inferData),
    };
  };
  /**
   * 转换外推数据
   * @param setting    设置
   * @param inferDatas 外推数据
   */
  mapInferData = (setting: ThemeSetting, inferDatas: Array<InferData>) => {
    return inferDatas
      .map((itemData) => {
        return this.mapInferMainStyle(setting, itemData);
      })
      .reduce((pre, next) => {
        return { ...pre, ...next };
      }, {});
  };
  /**
   * 转换外推数据
   * @param setting      主题设置对象
   * @param inferData    外推数据
   * @returns
   */
  mapInferDataStyle = (setting: ThemeSetting, inferData: InferData) => {
    const inferSetting = inferData.setting
      ? inferData.setting
      : setting.colorInferSetting;
    if (inferSetting.type === "color") {
      return Object.keys(inferSetting)
        .map((key: string) => {
          if (key === "light" || key === "dark") {
            return inferSetting[key]
              .map((l: any) => {
                const varName = this.getVarName(
                  setting,
                  inferSetting.type,
                  inferData.key,
                  key,
                  l.toString()
                );
                return {
                  [varName]: tinycolor(inferData.value)
                    .mix(
                      key === "light" ? this.colorWhite : this.colorBlack,
                      l * 10
                    )
                    .toHexString(),
                };
              })
              .reduce((pre: any, next: any) => {
                return { ...pre, ...next };
              }, {});
          }
          return {};
        })
        .reduce((pre, next) => {
          return { ...pre, ...next };
        }, {});
    }
    return {};
  };

  /**
   *
   * @param themeSetting 主题设置
   * @param keyValueData 键值数据
   * @returns            映射后的键值数据
   */
  mapKeyValue = (themeSetting: ThemeSetting, keyValueData: KeyValueData) => {
    return Object.keys(keyValueData)
      .map((key: string) => {
        return {
          [this.updateKeyBySetting(key, themeSetting)]: keyValueData[key],
        };
      })
      .reduce((pre, next) => {
        return { ...pre, ...next };
      }, {});
  };
  /**
   * 根据配置文件修改Key
   * @param key          key
   * @param themeSetting 主题设置
   * @returns
   */
  updateKeyBySetting = (key: string, themeSetting: ThemeSetting) => {
    return key.startsWith(themeSetting.startDivision)
      ? key
      : key.startsWith(themeSetting.namespace)
      ? themeSetting.startDivision + key
      : key.startsWith(themeSetting.division)
      ? themeSetting.startDivision + themeSetting.namespace
      : themeSetting.startDivision +
        themeSetting.namespace +
        themeSetting.division +
        key;
  };
  /**
   *
   * @param setting    主题设置
   * @param keyValue   主题键值对数据
   * @param inferDatas 外推数据
   * @returns 合并后的键值对数据
   */
  tokeyValueStyle = () => {
    return {
      ...this.mapInferData(this.themeSetting, this.inferData),
      ...this.mapKeyValue(this.themeSetting, this.keyValue),
    };
  };

  /**
   * 将keyValue对象转换为S
   * @param keyValue
   * @returns
   */
  toString = (keyValue: KeyValueData) => {
    const inner = Object.keys(keyValue)
      .map((key: string) => {
        return key + ":" + keyValue[key] + ";";
      })
      .join("");
    return `@charset "UTF-8";:root{${inner}}`;
  };

  /**
   *
   * @param elNewStyle 新的变量样式
   */
  writeNewStyle = (elNewStyle: string) => {
    if (this.isFirstWriteStyle) {
      const style = document.createElement("style");
      style.innerText = elNewStyle;
      document.head.appendChild(style);
      if (
        window.__MICRO_APP_BASE_APPLICATION__ &&
        document.getElementsByTagName("micro-app-head").length > 0
      ) {
        document.getElementsByTagName("micro-app-head")[0].appendChild(style);
      }
      this.isFirstWriteStyle = false;
    } else {
      if (document.head.lastChild) {
        document.head.lastChild.innerText = elNewStyle;
      }
    }
  };

  /**
   * 修改数据并且写入dom
   * @param updateInferData   平滑数据修改
   * @param updateKeyvalueData keyValue数据修改
   */
  updateWrite = (
    updateInferData?: UpdateInferData,
    updateKeyvalueData?: UpdateKeyValueData
  ) => {
    this.update(updateInferData, updateKeyvalueData);
    const newStyle = this.tokeyValueStyle();
    const newStyleString = this.toString(newStyle);
    this.writeNewStyle(newStyleString);
  };

  /**
   * 修改数据
   * @param inferData
   * @param keyvalueData
   */
  update = (
    updateInferData?: UpdateInferData,
    updateKeyvalueData?: UpdateKeyValueData
  ) => {
    if (updateInferData) {
      this.updateInferData(updateInferData);
    }
    if (updateKeyvalueData) {
      this.updateOrCreateKeyValueData(updateKeyvalueData);
    }
  };

  /**
   * 修改外推数据 外推数据只能修改,不能新增
   * @param inferData
   */
  updateInferData = (updateInferData: UpdateInferData) => {
    Object.keys(updateInferData).forEach((key) => {
      const findInfer = this.inferData.find((itemInfer) => {
        return itemInfer.key === key;
      });
      if (findInfer) {
        findInfer.value = updateInferData[key];
      } else {
        this.inferData.push({ key, value: updateInferData[key] });
      }
    });
  };

  /**
   * 初始化默认主题
   */
  initDefaultTheme = () => {
    this.updateWrite();
  };
  /**
   * 修改KeyValue数据
   * @param keyvalueData keyValue数据
   */
  updateOrCreateKeyValueData = (updateKeyvalueData: UpdateKeyValueData) => {
    Object.keys(updateKeyvalueData).forEach((key) => {
      const newKey = this.updateKeyBySetting(key, this.themeSetting);
      this.keyValue[newKey] = updateKeyvalueData[newKey];
    });
  };
}

const install = (app: App) => {
  app.config.globalProperties.theme = new Theme(
    setting,
    keyValueData,
    inferData
  );
};
export default { install };
